package com.wjh.serivce.impl;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson2.JSON;
import com.alipay.api.internal.util.AlipaySignature;
import com.wjh.base.ResponseApiCode;
import com.wjh.base.ResponseApiData;
import com.wjh.common.BaseInfo;
import com.wjh.common.CommonMethods;
import com.wjh.config.AlipayConfig;
import com.wjh.enums.PayResultEnum;
import com.wjh.enums.PayStateEnum;
import com.wjh.mapper.IPayRecordMapper;
import com.wjh.order.dto.OrderDetailDto;
import com.wjh.order.request.UpdateOrderPayStateRequest;
import com.wjh.pay.entity.PayRecordEntity;
import com.wjh.pay.enums.StateEnum;
import com.wjh.pay.request.PayOrderRequest;
import com.wjh.serivce.ICommonService;
import com.wjh.serivce.IPayCallbackService;
import com.wjh.serivce.IPayService;
import com.wjh.user.request.UpdateAmountRequest;
import com.wjh.util.SnowFlakeIdWorker;
import com.wjh.utils.AliPayHelper;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

/**
 * 支付Service实现类
 * 
 * @author wenjianhai
 * @date 2023/6/24
 * @since JDK 17
 */
@Slf4j
@Service
public class PayServiceImpl implements IPayService {

	@Autowired
	private AliPayHelper aliPayHelper;

	@Resource
	private AlipayConfig alipayConfig;

	/** 支付记录Mapper */
	@Autowired
	private IPayRecordMapper payRecordMapper;

	/** 支付回调Service接口类 */
	@Autowired
	private IPayCallbackService payCallbackService;

	@Autowired
	private ICommonService commonService;

	/** 雪花算法 */
	@Autowired
	private SnowFlakeIdWorker snowFlakeIdWorker;

	/**
	 * 订单支付
	 *
	 * @param request  支付请求参数
	 * @param baseInfo 当前登录人
	 * @date 2023-06-28
	 * @author wenjianhai
	 * @since JDK 17
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public ResponseApiData<?> payOrder(PayOrderRequest request, BaseInfo baseInfo) {
		// 订单Guid
		String orderGuid = request.getOrderGuid();
		// 查询订单及其详情
		OrderDetailDto order = commonService.queryOrderAndDetails(orderGuid);

		if (order == null) {
			log.error("订单支付,订单信息为空.订单Guid:{}", orderGuid);
			return ResponseApiData.systemFail("订单不存在");
		}
		if (!baseInfo.getUserCode().equals(order.getCreatorCode())) {
			log.error("订单支付,当前不是下单人.订单Guid:{}, 当前登录人:{}, 下单人:{}", orderGuid, baseInfo.getUserCode(),
					order.getCreatorCode());
			return ResponseApiData.systemFail("您不是下单人");
		}
		// 实付总金额（单位：分）
		long actualAmount = order.getActualAmount() == null ? 0L : order.getActualAmount();

		if (actualAmount <= 0) {
			log.info("订单支付,实付总金额小于或等于0.订单Guid:{}", orderGuid);
			return new ResponseApiData(new ResponseApiCode(ResponseApiCode.BUSINESS_ERROR.getCode(), "实付总金额小于或等于0"));
		}
		// 支付请求编号（请求第三方支付系统的编号，每次请求都要重新生成）
		String tradeNo = this.generateTradeNo();

		// 保存支付记录
		PayRecordEntity payRecord = this.savePayRecord(request.getPayMode(), order, tradeNo,
				PayResultEnum.PAY_ING.getCode(), PayResultEnum.PAY_ING.getName(), baseInfo);
		// 更新订单的支付状态
		this.updateOrderPayState(orderGuid, request.getPayMode(), PayStateEnum.PAY_ING.getCode());

		// 订单标题
		String subject = order.getOrderNo() + "订单支付";

		// 调用支付页面接口
		Map<String, Object> map = aliPayHelper.placeOrderAndPayForPCWeb(tradeNo, CommonMethods.fenToYuan(actualAmount),
				subject);

		log.info("订单支付,调用支付页面接口-结束,订单Guid:{}, 支付请求编号:{}, 实付总金额:{}, 返回结果:{}", orderGuid, tradeNo,
				actualAmount, JSON.toJSONString(map));

		// 是否调用成功
		boolean isSuccess = (Boolean) map.get("isSuccess");

		if (!isSuccess) {
			log.error("订单支付,调用支付页面接口-失败.订单Guid:{}, 支付请求编号:{}", orderGuid, tradeNo);
			// 支付记录
			payRecord.setPayResult(PayResultEnum.FAIL.getCode());
			payRecord.setPayRemark((String) map.get("subMsg"));
			// 更新支付记录
			payRecordMapper.updateForPay(payRecord);

			// 更新订单的支付状态
			this.updateOrderPayState(orderGuid, request.getPayMode(), PayStateEnum.FAIL.getCode());

			return ResponseApiData.systemFail("支付失败");
		}
		log.info("订单支付,调用支付页面接口-成功.订单Guid:{}, 支付请求编号:{}", orderGuid, tradeNo);
		// 订单支付成功处理
		payCallbackService.handlePaySuccess(payRecord, new HashMap<>(), "订单支付");

		log.info("订单支付-结束.请求参数:{}", JSON.toJSONString(request));

		return ResponseApiData.success();
	}

	/**
	 * 保存支付记录
	 *
	 * @date 2023-06-28
	 * @param payMode   支付方式（1:支付宝, 2:微信, 3:通联）
	 * @param order     订单信息
	 * @param payReqNo  支付请求编号（请求第三方支付系统的编号，每次请求都要重新生成）
	 * @param payResult 支付结果（0:支付中, 1:支付成功, 2:支付失败, 3:支付超时）
	 * @param payRemark 支付结果描述
	 * @param baseInfo  当前登录人
	 * @since JDK 17
	 * @author wenjianhai
	 */
	private PayRecordEntity savePayRecord(Integer payMode, OrderDetailDto order, String payReqNo, Integer payResult,
			String payRemark, BaseInfo baseInfo) {
		// 支付记录
		PayRecordEntity payRecord = new PayRecordEntity();
		payRecord.setRecordGuid(CommonMethods.getMeUUID());
		payRecord.setOrderGuid(order.getOrderGuid());
		payRecord.setOrderNo(order.getOrderNo());
		payRecord.setPayAmount(order.getActualAmount());
		payRecord.setPayMode(payMode);
		payRecord.setPayNo(CommonMethods.getNumber(10));
		payRecord.setPayReqNo(payReqNo);
		payRecord.setPayResult(payResult);
		payRecord.setPayRemark(payRemark);
		payRecord.setState(StateEnum.NORMAL.getCode());
		payRecord.setCreatorCode(baseInfo.getUserCode());
		payRecord.setCreatorName(baseInfo.getUserName());
		payRecord.setUpdaterCode(baseInfo.getUserCode());
		payRecord.setUpdaterName(baseInfo.getUserName());
		// 保存支付记录
		payRecordMapper.save(payRecord);

		return payRecord;
	}

	/**
	 * 订单支付回调处理
	 *
	 * @param requestParams 回调参数
	 * @date 2023-06-29
	 * @author wenjianhai
	 * @since JDK 17
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void callback(Map<String, String[]> requestParams) {
		log.info("订单支付回调处理-开始.回调参数:{}", JSON.toJSONString(requestParams));
		try {
			// 回调参数
			Map<String, String> params = CommonMethods.requestParamsToMap(requestParams);

			if (MapUtils.isEmpty(params)) {
				log.info("订单支付回调处理,回调参数为空");
				return;
			}
			log.info("订单支付回调处理,回调参数:{}", JSON.toJSONString(params));
			// 验签
			// 验证回调的正确性,是不是支付宝发的.并且要避免重复通知.
			params.remove("sign_type"); // remove这里看源码会发现，移不移除都没什么差，验签时会自动移除

			// 调用SDK验证签名
			boolean signVerified = AlipaySignature.rsaCheckV2(params, alipayConfig.getPublickey(), "utf-8", "RSA2");

			if (!signVerified) {
				// 验证失败
				log.error("订单支付回调处理,验证失败.回调参数:{}", JSON.toJSONString(params));
				throw new RuntimeException("非法请求，验证不通过");
			}
			// 商户订单号（支付请求编号）
			String outTradeNo = params.get("out_trade_no");

			// 查询支付信息
			PayRecordEntity payRecord = payRecordMapper.queryByPayReqNo(outTradeNo);

			if (payRecord == null) {
				log.error("订单支付回调处理,支付记录已不存在.支付请求编号:{}", outTradeNo);
				throw new RuntimeException("支付记录已不存在");
			}
			if (PayResultEnum.SUCCESS.getCode().equals(payRecord.getPayResult())) {
				log.info("订单支付回调处理,订单已支付成功.支付请求编号:{}, 订单Guid:{}, 订单号:{}", outTradeNo,
						payRecord.getOrderGuid(), payRecord.getOrderNo());
				return;
			}
			// 订单支付成功处理
			payCallbackService.handlePaySuccess(payRecord, params, "订单支付回调");
		} catch (Exception e) {
			log.error(String.format("订单支付回调处理-失败.回调参数:%s", JSON.toJSONString(requestParams)), e);
			throw new RuntimeException("订单支付回调处理失败");
		}
	}

	/**
	 * 更新订单的支付状态
	 *
	 * @date 2023-06-28
	 * @param orderGuid 订单Guid
	 * @param payMode   支付方式（1:支付宝, 2:微信, 3:通联）
	 * @param payState  支付状态（0:未支付, 1:支付中, 2:支付成功, 3:支付失败）
	 * @since JDK 17
	 * @author wenjianhai
	 */
	private void updateOrderPayState(String orderGuid, Integer payMode, Integer payState) {
		UpdateOrderPayStateRequest request = UpdateOrderPayStateRequest.builder().orderGuid(orderGuid).payMode(payMode)
				.payState(payState).build();
		commonService.updateOrderPayState(request, "订单支付");
	}

	/**
	 * 更新用户可用金额
	 * 
	 * @date 2023-06-29
	 * @param userCode 用户编码
	 * @param amount   金额（单位：分）
	 * @param flag     标识（1:增加, 2:扣除）
	 * @param tip      功能名称
	 * @since JDK 17
	 * @author wenjianhai
	 */
	private void updateUserAmount(String userCode, Long amount, int flag, String tip) {
		UpdateAmountRequest request = UpdateAmountRequest.builder().userCode(userCode).amount(amount).flag(flag)
				.tip(tip).build();

		commonService.updateUserAmount(request);
	}

	/**
	 * 生成支付请求编号
	 *
	 * @date 2023-06-28
	 * @return String
	 * @since JDK 17
	 * @author wenjianhai
	 */
	private String generateTradeNo() {
		String tradeNo = String.valueOf(this.snowFlakeIdWorker.nextId());
		int intMeLen = 22 - tradeNo.length();
		if (intMeLen > 0) {
			tradeNo = tradeNo + CommonMethods.getNumber(intMeLen);
		}
		return tradeNo;
	}
}
